package mission2.model;


/**
 * @author julien colmonts
 */

public class FormalExpressionTreeImpl extends LinkedRBinaryTree<String> implements FormalExpressionTree{
	
	public FormalExpressionTreeImpl(BTNode<String> root) {
		super(root);
	}

	public FormalExpressionTreeImpl(String element) {
		super(element);
	}

	public FormalExpressionTreeImpl derive(){
		BTNode<String> newRoot = deriveAux((BTNode<String>) this.root());
		return new FormalExpressionTreeImpl(newRoot);
	}
	
	public static BTNode<String> deriveAux(BTNode<String> node) {
		if(node.element().equals("+") || node.element().equals("-")){
			// Derivee d'une somme/diff est la somme/diff des derivees.
			return new BTNode<String>(node.element(), deriveAux((BTNode<String>) node.getLeft()), deriveAux((BTNode<String>) node.getRight()), node.getParent()); 
		}
		else if(node.element().equals("*")) {
			// Derivee d'un produit : (f*g)' = f' * g + f * g'
			BTNode<String> exprLeft = new BTNode<String>("*",deriveAux((BTNode<String>) node.getLeft()), node.getRight(),null);
			BTNode<String> exprRight = new BTNode<String>("*",node.getLeft(), deriveAux((BTNode<String>) node.getRight()),null);
			BTNode<String> expr = new BTNode<String>("+", exprLeft,exprRight, node.getParent());
			
			exprLeft.setParent(expr);
			exprRight.setParent(expr);
			return expr;
		}
		else if(node.element().equals("/")) {
			// Derivee d'une difference : (f/g)' = (f' * g - f * g')/g^2
			BTNode<String> exprLeft = new BTNode<String>("*",deriveAux((BTNode<String>) node.getLeft()), node.getRight(),null);
			BTNode<String> exprRight = new BTNode<String>("*",node.getLeft(), deriveAux((BTNode<String>) node.getRight()),null);
			BTNode<String> num = new BTNode<String>("-", exprLeft,exprRight, null);
			
			exprLeft.setParent(num);
			exprRight.setParent(num);
			
			BTNode<String> power = new BTNode<String>("2", null, null, null);
			BTNode<String> denom = new BTNode<String>("^", node.getRight(),power, null);
			
			power.setParent(denom);
			
			BTNode<String> expr = new BTNode<String>("/", num, denom, node.getParent());
			num.setParent(expr);
			denom.setParent(expr);
			return expr;
		}
		else if(node.element().equals("^")) {
			// Derivee d
			BTNode<String> expr1 = new BTNode<String>("*", node.getRight(), deriveAux((BTNode<String>) node.getLeft()), null);
			
			int power = (Integer.valueOf(node.getRight().element()));
			power--;
			String powerReduced = Integer.toString(power);
			BTNode<String> powerReducedNode = new BTNode<String>(powerReduced, null, null, null);
			BTNode<String> expr2 = new BTNode<String>("^", node.getLeft(), powerReducedNode , null);
			powerReducedNode.setParent(expr2);
			BTNode<String> expr = new BTNode<String>("*", expr1, expr2, node.getParent());
			
			expr1.setParent(expr);
			expr2.setParent(expr);
			return expr;
		}
		else if(node.element().equals("sin")) {
			BTNode<String> sinDerivExpr = new BTNode<String>("cos", node.getLeft(), null, null);
			BTNode<String> expr = new BTNode<String> ("*", deriveAux((BTNode<String>) node.getLeft()), sinDerivExpr, node.getParent());
			
			sinDerivExpr.setParent(expr);
			return expr;
		}
		else if(node.element().equals("cos")){
			BTNode<String> cosDerivExpr1 = new BTNode<String>("sin", node.getLeft(),null,null);
			BTNode<String> cosDerivExpr2 = new BTNode<String>("*", deriveAux((BTNode<String>) node.getLeft()), cosDerivExpr1, null);
			
			BTNode<String> zero = new BTNode<String>("0",null,null,null);
			BTNode<String> expr = new BTNode<String>("-", zero, cosDerivExpr2, node.getParent());
			
			cosDerivExpr1.setParent(cosDerivExpr2);
			zero.setParent(expr);
			cosDerivExpr2.setParent(expr);
			return expr;
		}
		else if(Utils.isAnInteger(node.element())) {
			return new BTNode<String>("0", null,null, node.getParent());
		}
		else if(node.element().matches("[a-z]{1}")) {
			return new BTNode<String>("1", null,null,node.getParent());
		}
		else {
			return null; // never read because of pre/post
		}
	}

	public String toString(){
		if(this.isLeaf()){
			return this.root().element();
		}else if(this.root().element().equals("cos") || this.root().element().equals("sin")){
			
			return this.root().element() + "(" + new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getLeft()) +")";
		}else{
			if(Utils.isAnInteger(((BTNode<String>) root()).getLeft().element()) || ((BTNode<String>) root()).getLeft().element().matches("[a-z]{1}")){
				if(Utils.isAnInteger(((BTNode<String>) root()).getRight().element()) || ((BTNode<String>) root()).getRight().element().matches("[a-z]{1}")){
					return new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getLeft())+ this.root().element() + new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getRight());
				}else{
					return new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getLeft())+ this.root().element() + "(" + new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getRight()) + ")";
				}
			}else if(Utils.isAnInteger(((BTNode<String>) root()).getRight().element()) || ((BTNode<String>) root()).getRight().element().matches("[a-z]{1}")){
				return "(" + new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getLeft()) + ")" + this.root().element() +new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getRight());
			}else{
				return "(" + new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getLeft()) + ")" + this.root().element() + "(" + new FormalExpressionTreeImpl((BTNode<String>) ((BTNode<String>) root()).getRight()) + ")";
			}
		}
	}
	
}
